#if !USE_CONSOLE

#include "src/interface/treeWidget.h"

#include "src/interface/glwidget.h"
#include "src/interface/objectTreeItem.h"
#include "src/interface/lightTreeItem.h"
#include "src/interface/treeItem.h"
#include "src/interface/lightDialog.h"
#include "src/interface/objectDialog.h"
#include "src/interface/operationDialog.h"
#include "src/interface/scaleDialog.h"
#include "src/interface/rotateDialog.h"
#include "src/interface/translateDialog.h"

#include "src/scene/object.h"
#include "src/scene/light.h"
#include "src/scene/combinedObject.h"
#include "src/scene/cuboidPrimitive.h"
#include "src/scene/spherePrimitive.h"
#include "src/scene/cylinderPrimitive.h"
#include "src/scene/conePrimitive.h"

#include "src/defines/primitive.h"

#include <iostream>

TreeWidget::TreeWidget(const QString &_name, GLWidget *_glWidget, QWidget *_parent) : QTreeWidget(_parent), inOperation(false), glWidget(_glWidget)
{
//    setHeaderHidden(true);
    setContextMenuPolicy(Qt::ActionsContextMenu);
    setSelectionMode(QAbstractItemView::ExtendedSelection);
    setSelectionBehavior(QAbstractItemView::SelectRows);
    setAnimated(true);
    setExpandsOnDoubleClick(false);

    connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(itemDoubleClickedSlot(QTreeWidgetItem *, int)));
    connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(itemSelectionChangedSlot()));


    headerItem()->setText(0, _name);
}

TreeWidget::~TreeWidget()
{
}

void TreeWidget::addObject(Object *_obj)
{
    QTreeWidgetItem *rootItem = invisibleRootItem();
    QTreeWidgetItem *newItem = new ObjectTreeItem(rootItem, _obj);

    for(int i = 0; i < previousSelected.size(); i++){
        QTreeWidgetItem *item = previousSelected[i];
        item->setSelected(false);
        ((TreeItem *)item)->select(false);
    }
    previousSelected.clear();

    newItem->setSelected(true);
}

void TreeWidget::addLight(Light *_l)
{
    QTreeWidgetItem *rootItem = invisibleRootItem();
    QTreeWidgetItem *newItem = new LightTreeItem(rootItem, _l);

    for(int i = 0; i < previousSelected.size(); i++){
        QTreeWidgetItem *item = previousSelected[i];
        item->setSelected(false);
        ((TreeItem *)item)->select(false);
    }
    previousSelected.clear();

    newItem->setSelected(true);
}

void TreeWidget::deleteItens(Scene *_scene){
    if(previousSelected.empty()) return;

    inOperation = true;

    int type = previousSelected[0]->getType();

    if(type == OBJECT_ITEM)
    {
        for(int i = previousSelected.size(); i > 0 ; i--)
        {
            TreeItem *item = previousSelected.at(i - 1);

            Object *obj = ((ObjectTreeItem *)item)->getObject();

            /*se o pai desse objeto não for o root,
            significa que ele eh uma composição,
            então tem que deletar esse objeto la também*/

            QTreeWidgetItem *parent = item->parent();
            if(parent == NULL){
                _scene->deleteObject(obj);
                invisibleRootItem()->removeChild(item);
            }
            else{
                Object *parentObj = (CombinedObject *)((ObjectTreeItem *)parent)->getObject();
                ((CombinedObject *)parentObj)->deleteObject(obj);
                parent->removeChild(item);

                if(parent->childCount() == 1){
                    QTreeWidgetItem *otherItem = parent->child(0);
                    parent->removeChild(otherItem);
                    Object *otherItemObj = ((ObjectTreeItem *)otherItem)->getObject();

                    QTreeWidgetItem *grandParent = parent->parent();
                    if(grandParent == NULL){ //coloca na cena o objeto e na arvore principal
                        invisibleRootItem()->addChild(otherItem);
                        _scene->addObject(otherItemObj);

                        invisibleRootItem()->removeChild(parent);
                        _scene->deleteObject(parentObj);

                    }else{
                        Object *grandParentObject = ((CombinedObject *)((ObjectTreeItem *)grandParent)->getObject());

                        grandParent->addChild(otherItem);
                        ((CombinedObject *)grandParentObject)->addObject(otherItemObj);

                        grandParent->removeChild(parent);
                        ((CombinedObject *)grandParentObject)->deleteObject(parentObj);

                        //atualiznado o pai mais acima
                        while(grandParent->parent() != NULL){
                            grandParent = grandParent->parent();
                        }

                    }

                    delete parent;
                    delete parentObj;
                }
            }

            delete item;
            delete obj;

            previousSelected.clear();
            emit updateActionsFromChanging();

        }
    }
    else //if(type == LIGHT_ITEM)
    {
        //nunca apaga a primeira luz!
        for(int i = previousSelected.size(); i > 0 ; i--)
        {
            TreeItem *item = previousSelected.at(i - 1);

            Light *light = ((LightTreeItem *)item)->getLight();
            if(light->getType() != AMBIENT_LIGHT){
                _scene->deleteLight(light);
                invisibleRootItem()->removeChild(item);

                delete item;
                delete light;
            }

            previousSelected.clear();
            updateActionsFromLightSlot();
        }
    }


    inOperation = false;
}

void TreeWidget::duplicateObject(QTreeWidgetItem *_parent, Object *_obj){
    QTreeWidgetItem *item = new ObjectTreeItem(_parent, _obj);

    if(!_obj->isPrimitive()){
        for(unsigned int i = 0; i < ((CombinedObject *)_obj)->getObjectsSize(); i++){
            duplicateObject(item, ((CombinedObject *)_obj)->getObject(i));
        }
    }
}

Object *TreeWidget::duplicateObject(Object *_obj, Scene *_scene){

    Object *newObj = NULL;

    if(_obj->isPrimitive()){
        switch(_obj->getType()){
            case CUBOID:{
                newObj = new CuboidPrimitive((*((CuboidPrimitive *)_obj)), _scene->nextObjectsID());
                break;
            }
            case SPHERE:{
                newObj = new SpherePrimitive((*((SpherePrimitive *)_obj)), _scene->nextObjectsID());
                break;
            }
            case CYLINDER:{
                newObj = new CylinderPrimitive((*((CylinderPrimitive *)_obj)), _scene->nextObjectsID());
                break;
            }
            case CONE:{
                newObj = new ConePrimitive((*((ConePrimitive *)_obj)), _scene->nextObjectsID());
                break;
            }
        }
    }else{
        std::vector<Object *> newChildrens;
        for(unsigned int i = 0; i < ((CombinedObject *)_obj)->getObjectsSize(); i++){
            newChildrens.push_back( duplicateObject(((CombinedObject *)_obj)->getObject(i), _scene));
        }
        newObj = new CombinedObject((*((CombinedObject *)_obj)), newChildrens, _scene->nextObjectsID());

    }

    return newObj;
}

void TreeWidget::duplicateObject(Scene *_scene){
    //é garantido que tenha um unico selecionado
    QTreeWidgetItem *item = previousSelected.front();
    Object *obj = ((ObjectTreeItem *)item)->getObject();

    QTreeWidgetItem *parent = item->parent();

    Object *newObj = duplicateObject(obj, _scene);

    if(!parent){
        _scene->addObject(newObj);
        parent = invisibleRootItem();
    }else{
        Object *parentObject = ((ObjectTreeItem *)parent)->getObject();
        ((CombinedObject *)parentObject)->addObject(newObj);
    }

    duplicateObject(parent, newObj);


    updateActionsFromChanging();
}

void TreeWidget::itemSelectionChangedSlot(){
    if(inOperation) return;

    inOperation = true;


    //descelecionando os anteriores
    for(int i = 0; i < previousSelected.size(); i++){
        QTreeWidgetItem *item = previousSelected[i];
        ((TreeItem *)item)->select(false);
    }
    previousSelected.clear();

    QList<QTreeWidgetItem *> selected = selectedItems();
    //selecionando de fato os novos
    for(int i = 0; i < selected.size(); i++){
        TreeItem *item = (TreeItem *)selected[i];
        item->select(true);
        previousSelected.push_back(item);
    }

    emit updateActionsFromSelection(selected.size());
    updateActionsFromLightSlot();

    inOperation = false;
}

void TreeWidget::itemDoubleClickedSlot(QTreeWidgetItem * _item, int){
//    _item->setSelected(false);
    inOperation = true;


    unsigned int type = ((TreeItem *)_item)->getType();
    if(type == OBJECT_ITEM){
        Object *obj = ((ObjectTreeItem *)_item)->getObject();
        if(obj->isPrimitive()){
            ObjectDialog *dlg = new ObjectDialog(obj, (ObjectTreeItem *)_item, glWidget, this);
            dlg->show();
            connect(dlg, SIGNAL(done()), this, SLOT(updateActionsFromEditingSlot()));
        }
        else{
            OperationDialog *dlg = new OperationDialog((CombinedObject *)obj, (ObjectTreeItem *)_item, invisibleRootItem(), glWidget, this);
            dlg->show();
            connect(dlg, SIGNAL(done()), this, SLOT(updateActionsFromEditingSlot()));
            connect(dlg, SIGNAL(changed()), this, SIGNAL(updateActionsFromChanging()));
            connect(dlg, SIGNAL(deleteObject(Object *)), this, SLOT(deleteObjectSlot(Object *)));
            connect(dlg, SIGNAL(addObject(Object *)), this, SLOT(addObjectSlot(Object *)));
        }
    }else //if(type == LIGHT_ITEM){
    {
        Light *light = ((LightTreeItem *)_item)->getLight();
        LightDialog *dlg = new LightDialog(light, (LightTreeItem *)_item, this);
        dlg->show();
        connect(dlg, SIGNAL(done()), this, SLOT(updateActionsFromLightSlot()));
    }


    inOperation = false;
}

int TreeWidget::addTreeItem(Object *_obj, QTreeWidgetItem *_item, bool _setId, Scene *_scene){
    if(_setId){
        _obj->setId(_scene->nextObjectsID());
    }

    QTreeWidgetItem *newItem = new ObjectTreeItem(_item, _obj);

    if(!_obj->isPrimitive()){
        int currentMaxId, maxId = -1;
        for(unsigned int i = 0; i < ((CombinedObject *)_obj)->getObjectsSize(); i++){
            currentMaxId = addTreeItem(((CombinedObject *)_obj)->getObject(i), newItem, _setId, _scene);
            if(currentMaxId > maxId) maxId = currentMaxId;
        }
        return maxId;
    }else return _obj->getId();
}

void TreeWidget::updateObjects(const std::vector<Object *> *_objs, Scene *_scene){
    inOperation = true;
    QTreeWidgetItem *rootItem = invisibleRootItem();


    bool setId = (_scene->getObjectsSize() > 0);

    int currentMaxId, maxId = -1;
    bool reorganizeIds = false;
    if(!_objs){
        clear();
        _objs = &_scene->getObjects();
    }
    else reorganizeIds = true;
    for(unsigned int i = 0; i < _objs->size(); i++){
        Object *obj = _objs->at(i);

        if(reorganizeIds) _scene->addObject(obj);

        currentMaxId = addTreeItem(obj, rootItem, setId, _scene);
        if(currentMaxId > maxId) maxId = currentMaxId;
    }
    _scene->setNextObjectsID(maxId);

    inOperation = false;
}

void TreeWidget::updateLights(Scene *_scene){
    inOperation = true;
    previousSelected.clear();
    clear();
    QTreeWidgetItem *rootItem = invisibleRootItem();

    const std::vector<Light *> &lights = _scene->getLights();


    for(unsigned int i = 0; i < lights.size(); i++){
        new LightTreeItem(rootItem, lights.at(i));
    }

    inOperation = false;
}

std::vector<Object *> TreeWidget::preOperation(QTreeWidgetItem **parent){
    inOperation = true;

    *parent = previousSelected.at(0)->parent();
    std::vector<Object *> objects;
    objects.push_back(((ObjectTreeItem *)previousSelected.at(0))->getObject());
//    std::cout << "pegando o objeto " << ((ObjectTreeItem *)previousSelected.at(0))->getObject()->getId() << std::endl;

//    std::cout << "pre operation" << std::endl;

    for(int i = 1; i < previousSelected.size(); i++){
        QTreeWidgetItem *item = previousSelected.at(i);
        objects.push_back(((ObjectTreeItem *)item)->getObject());

//        std::cout << "pegando o objeto " << ((ObjectTreeItem *)item)->getObject()->getId() << std::endl;

        if(item->parent() != *parent) {
            QMessageBox::information(this, QString::fromUtf8("Erro na operação"), QString::fromUtf8("Escolha objetos com o mesmo pai."));
            objects.clear();
            break;
        }
    }
    if(*parent){
        if((*parent)->childCount() == previousSelected.size()){
            QMessageBox::information(this, QString::fromUtf8("Erro na operação"), QString::fromUtf8("Não se pode fazer uma operação com todos os filhos."));
            objects.clear();
        }
    }

    inOperation = false;
    return objects;
}

void TreeWidget::postOperation(QTreeWidgetItem *_parent, Object *_obj, Scene *_scene){
    Object *parentObject = NULL;
    if(_parent == NULL){
        _parent = invisibleRootItem();
        _scene->addObject(_obj);
    }else{
        parentObject = ((ObjectTreeItem *)_parent)->getObject();
        ((CombinedObject *)parentObject)->addObject(_obj);
    }
    QTreeWidgetItem *newCombinedItem = new ObjectTreeItem(_parent, _obj);

    int size = previousSelected.size();
    for(int i = 0; i < size; i++){
        QTreeWidgetItem *item = previousSelected.at(i);
        Object *currentObj = ((ObjectTreeItem *)item)->getObject();
        currentObj->select(false);
        if(!parentObject){
            _scene->deleteObject(currentObj);
        }else{
            ((CombinedObject *)parentObject)->deleteObject(currentObj);
        }


        _parent->removeChild(item);
        newCombinedItem->addChild(item);
        item->setExpanded(true);
    }


    previousSelected.clear();
    updateActionsFromChanging();
    inOperation = false;
}

void TreeWidget::unionOperation(Scene *_scene){
    QTreeWidgetItem *parent;
    std::vector<Object *> objects = preOperation(&parent);
    if(objects.empty()) return;

//    int size = objects.size();
//    for(int i = 0; i < size; i++){QTreeWidgetItem *item = previousSelected.at(i);
//        Object *currentObj = ((ObjectTreeItem *)item)->getObject();
//        std::cout << "Objeto " << currentObj->getId() << std::endl;
//    }
    postOperation(parent, new CombinedObject(UNION, objects, objects.at(0)->getMaterial(), _scene->nextObjectsID()), _scene);
}

void TreeWidget::intersectionOperation(Scene *_scene){
    QTreeWidgetItem *parent;
    std::vector<Object *> objects = preOperation(&parent);
    if(objects.empty()) return;

    postOperation(parent, new CombinedObject(INTERSECTION, objects, objects.at(0)->getMaterial(), _scene->nextObjectsID()), _scene);
}

void TreeWidget::differenceOperation(Scene *_scene){
    QTreeWidgetItem *parent;
    std::vector<Object *> objects = preOperation(&parent);
    if(objects.empty()) return;

    postOperation(parent, new CombinedObject(DIFFERENCE, objects, objects.at(0)->getMaterial(), _scene->nextObjectsID()), _scene);
}

void TreeWidget::translateOperation(){
    TranslateDialog *dlg = new TranslateDialog(previousSelected);
    dlg->show();
//    previousSelected.clear();

//    if(rayTestViewAct->isChecked()){
//        rayTestViewAct->setChecked(false);
//        scene->clearTestRay();
//        canvas->updateGL();
//    }

    connect(dlg, SIGNAL(done()), this, SLOT(updateActionsFromChangingSlot()));

//    updateActionsFromChanging(previousSelected.size());
}

void TreeWidget::rotateOperation(){
    RotateDialog *dlg = new RotateDialog(previousSelected);
    dlg->show();
//    previousSelected.clear();

//    if(rayTestViewAct->isChecked()){
//        rayTestViewAct->setChecked(false);
//        scene->clearTestRay();
//        canvas->updateGL();
//    }
    connect(dlg, SIGNAL(done()), this, SLOT(updateActionsFromChangingSlot()));
//    updateActionsFromChanging(previousSelected.size());
}

void TreeWidget::scaleOperation(){
    ScaleDialog *dlg = new ScaleDialog(previousSelected);
    dlg->show();
//    previousSelected.clear();

//    if(rayTestViewAct->isChecked()){
//        rayTestViewAct->setChecked(false);
//        scene->clearTestRay();
//        canvas->updateGL();
//    }
    connect(dlg, SIGNAL(done()), this, SLOT(updateActionsFromChangingSlot()));
//    updateActionsFromChanging(previousSelected.size());
}

#endif //!USE_CONSOLE
